/*
 * Copyright 2025-2026 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package hawk.demo;

import glz.hawkgenerator.java.*;
import glz.hawkgenerator.java.type.ArrayTypeName;
import glz.hawkgenerator.java.type.ParameterizedTypeName;
import glz.hawkgenerator.java.type.WildcardTypeName;
import hawk.util.ProjectUtils;

import javax.lang.model.element.Modifier;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static glz.hawkgenerator.java.type.PrimitiveTypeName.*;
import static glz.hawkgenerator.java.type.VoidTypeName.VOID;

/**
 * This class is responsible for
 *
 * @author Hawk
 */
public class CodeAndControlFlowGenerator {
    private final String packageName;

    public CodeAndControlFlowGenerator(String packageName) {
        this.packageName = packageName;
    }

    public void generateCodeAndControlFlow() {

        FieldSpec fieldSpec1 = FieldSpec.builder(String.class, "name", Modifier.PUBLIC).build();
        FieldSpec fieldSpec2 = FieldSpec.builder(ParameterizedTypeName.of(Class.class, WildcardTypeName.of()), "clazz", Modifier.PUBLIC).build();
        FieldSpec fieldSpec3 = FieldSpec.builder(INT, "count", Modifier.PUBLIC).build();
        FieldSpec fieldSpec4 = FieldSpec.builder(ParameterizedTypeName.of(Map.class, String.class, BigDecimal.class), "map", Modifier.PUBLIC).build();

        ClassSpec classSpec = ClassSpec.builder("CodeAndControlFlowExample", Modifier.PUBLIC)
                .addField(fieldSpec1)
                .addField(fieldSpec2)
                .addField(fieldSpec3)
                .addField(fieldSpec4)
                .addConstructor(ConstructorSpec.builder(Modifier.PUBLIC)
                        .beginConstructorBody()
                        .addStatement("this.$N = $S", fieldSpec1, "cat")
                        .addStatement("this.$N = $T.class", fieldSpec2, Map.class)
                        .addStatement("this.$N = $L", fieldSpec3, 1000)
                        .addStatement("this.$N = new $T<>()", fieldSpec4, HashMap.class)
                        .end()
                        .build())
                .addMethod(MethodSpec.builder(LONG, "forExample1", Modifier.PUBLIC)
                        .addParameter(LONG, "a")
                        .addParameter(LONG, "b")
                        .beginMethodBody()
                        .addStatement("long sum = 0")
                        .beginFor("long i = 0; i < a; i++")
                        .beginFor("long j = 0; j < b; j++")
                        .addStatement("sum = sum + i * j")
                        .endFor()
                        .endFor()
                        .addStatement("return sum")
                        .end()
                        .build())
                .addMethod(MethodSpec.builder(LONG, "forExample2", Modifier.PUBLIC)
                        .addParameter(ArrayTypeName.ofTypeName(LONG), "a")
                        .addParameter(ArrayTypeName.ofTypeName(LONG), "b")
                        .beginMethodBody()
                        .addStatement("long sum = 0")
                        .beginFor("long i: a")
                        .beginFor("long j: b")
                        .addStatement("sum = sum + i * j")
                        .endFor()
                        .endFor()
                        .addStatement("return sum")
                        .end()
                        .build())
                .addMethod(MethodSpec.builder(VOID, "ifExample", Modifier.PUBLIC)
                        .addParameter(INT, "a")
                        .beginMethodBody()
                        .beginIf("a < 5")
                        .addStatement("System.out.println($S)", "The parameter is less than 5")
                        .beginElseIf("a < 100")
                        .addStatement("System.out.println($S)", "The parameter is greater or equal to 5 and less than 100")
                        .beginElse()
                        .addStatement("System.out.println($S)", "The parameter is greater than or equal to 100")
                        .endIf()
                        .end()
                        .build())
                .addMethod(MethodSpec.builder(INT, "whileExample", Modifier.PUBLIC)
                        .addParameter(INT, "a")
                        .beginMethodBody()
                        .addStatement("int b = a > 0 ? a : -a")
                        .beginWhile("b >= 3")
                        .addStatement(" b = b / 3")
                        .endWhile()
                        .addStatement("return b")
                        .end()
                        .build())
                .addMethod(MethodSpec.builder(INT, "modulo", Modifier.PUBLIC)
                        .addParameter(INT, "a")
                        .addParameter(INT, "b")
                        .beginMethodBody()
                        .addStatement("a = $T.abs(a)", Math.class)
                        .addStatement("b = $T.abs(b)", Math.class)
                        .beginDo("a > b")
                        .addStatement("a = a / b")
                        .endDo()
                        .addStatement("return a")
                        .end()
                        .build())
                .addMethod(MethodSpec.builder(VOID, "tryExample1", Modifier.PUBLIC)
                        .addAnnotation(AnnotationInstanceSpec.builder(SuppressWarnings.class).addMember("value", "CallToPrintStackTrace").build())
                        .addParameter(File.class, "file")
                        .beginMethodBody()
                        .beginTry("$T br = new $T(new $T(file))", BufferedReader.class, BufferedReader.class, FileReader.class)
                        .addStatement("String line")
                        .beginWhile("(line = br.readLine()) != null")
                        .addStatement("System.out.println(line)")
                        .endWhile()
                        .beginCatch("IOException e")
                        .addStatement("e.printStackTrace()")
                        .endTry()
                        .end()
                        .build())
                .addMethod(MethodSpec.builder(VOID, "tryExample2", Modifier.PUBLIC)
                        .addAnnotation(AnnotationInstanceSpec.builder(SuppressWarnings.class).addMember("value", "CallToPrintStackTrace").build())
                        .addParameter(File.class, "file")
                        .beginMethodBody()
                        .addStatement("$T br = null", BufferedReader.class)
                        .beginTry()
                        .addStatement("br = new $T(new $T(file))", BufferedReader.class, FileReader.class)
                        .addStatement("String line")
                        .beginWhile("(line = br.readLine()) != null")
                        .addStatement("System.out.println(line)")
                        .endWhile()
                        .beginCatch("IOException e")
                        .addStatement("e.printStackTrace()")
                        .beginFinally()
                        .beginIf("br != null")
                        .beginTry()
                        .addStatement("br.close()")
                        .beginCatch("$T e", IOException.class)
                        .addStatement("e.printStackTrace()")
                        .endTry()
                        .endIf()
                        .endTry()
                        .end()
                        .build())
                .addMethod(MethodSpec.builder(VOID, "switchExample", Modifier.PUBLIC)
                        .addParameter(CHAR, "ch")
                        .beginMethodBody()
                        .beginSwitch("ch")
                        .beginCase("'a'")
                        .addStatement("System.out.println($S)", "Found a")
                        .breakCase()
                        .beginCase("'b'")
                        .addStatement("System.out.println($S)", "Found b")
                        .breakCase()
                        .beginCase("'c'")
                        .endCase()
                        .beginCase("'d'")
                        .addStatement("System.out.println($S)", "Found c and d")
                        .breakCase()
                        .beginDefault()
                        .addStatement("System.out.println($S)", "Found other char")
                        .endSwitch()
                        .end()
                        .build())
                .addMethod(MethodSpec.builder(VOID, "complexExample1", Modifier.PUBLIC)
                        .addParameter(ParameterizedTypeName.of(List.class, File.class), "fileList")
                        .beginMethodBody()
                        .beginFor("$T file : fileList", File.class)
                        .beginTry("$T br = new $T(new $T(file))", BufferedReader.class, BufferedReader.class, FileReader.class)
                        .addStatement("String line")
                        .beginWhile("(line = br.readLine()) != null")
                        .addStatement("System.out.println(line)")
                        .endWhile()
                        .beginCatch("IOException e")
                        .addStatement("e.printStackTrace()")
                        .endTry()
                        .endFor()
                        .end()
                        .build())
                .build();

        ProjectUtils.write(packageName, classSpec);

    }
}
